package pen.gesture.input;

import android.app.Dialog;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.os.Bundle;
import android.os.AsyncTask;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.View;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.View.OnKeyListener;
import android.gesture.GestureLibrary;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.ArrayAdapter;
import android.content.DialogInterface;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.text.TextUtils;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;

import java.io.File;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Comparator;
import java.util.Set;
	
public class GestureBuilderActivity extends ListActivity {
	private static final int STATUS_SUCCESS = 0;
	private static final int STATUS_CANCELLED = 1;
	private static final int STATUS_NO_STORAGE = 2;
	private static final int STATUS_NOT_LOADED = 3;
	
	private static final int MENU_ID_RENAME = 1;
	private static final int MENU_ID_REMOVE = 2;
	
	private static final int DIALOG_RENAME_GESTURE = 1;
	
	private static final int REQUEST_NEW_GESTURE = 1;
	
	private static final String GESTURES_INFO_ID = "gestures.info_id";
	
	private final Comparator<NamedGesture> mSorter = new Comparator<NamedGesture>() {
		public int compare(NamedGesture object1, NamedGesture object2) {
			return object1.name.compareTo(object2.name);
		}
	};
	
	private static GestureLibrary sStore;
	
	private GesturesAdapter mAdapter;
	private GesturesLoadTask mTask;
	private TextView mEmpty;
	
	private Dialog mRenameDialog;
	private EditText mInput;
	private NamedGesture mCurrentRenameGesture;
	private EditText mName;
	private String mGesturesName;
	private File mLoadedGestures;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.gestures_list);
		
		mAdapter = new GesturesAdapter(this);
		setListAdapter(mAdapter);
		
		// Получаем название языка
		mGesturesName = getIntent().getDataString();

		mName = (EditText) findViewById(R.id.list_name);
		mEmpty = (TextView) findViewById(android.R.id.empty);
		
		if(mGesturesName != null) {
			// Открываем жесты из файла
			mLoadedGestures = new File(mGesturesName);
			mName.setText(mLoadedGestures.getName());
		} else {
			// Создаём новый временный файл с жестами
			mLoadedGestures = new File(getCacheDir(), "gestures");
			mName.setVisibility(View.VISIBLE);
		}
		
		sStore = GestureLibraries.fromFile(mLoadedGestures);
		loadGestures();
		
		mName.setOnKeyListener(new OnKeyListener() {
			@Override
			public boolean onKey(View v, int keyCode, KeyEvent event) {
				// Ввод имени языка
				if(mName.getText().length() > 0) {
					mGesturesName = mName.getText().toString();
				}
				return false;
			}
		});
		
		registerForContextMenu(getListView());
	}
	
	static GestureLibrary getStore() {
		return sStore;
	}
	
	public void reloadGestures(View v) {
		loadGestures();
	}
	
	public void addGesture(View v) {
		Intent intent = new Intent(this, CreateGestureActivity.class);
		startActivityForResult(intent, REQUEST_NEW_GESTURE);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		
		if (resultCode == RESULT_OK) {
			switch (requestCode) {
			case REQUEST_NEW_GESTURE:
				loadGestures();
				break;
			}
		}
	}
	
	private void loadGestures() {
		if (mTask != null && mTask.getStatus() != GesturesLoadTask.Status.FINISHED) {
			mTask.cancel(true);
		}
		mTask = (GesturesLoadTask) new GesturesLoadTask().execute();
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		
		if (mTask != null && mTask.getStatus() != GesturesLoadTask.Status.FINISHED) {
			mTask.cancel(true);
			mTask = null;
		}
		
		setResult(RESULT_OK);
		cleanupRenameDialog();
		
		// Сохраняем язык в нужный файл
		if(mGesturesName != null && mGesturesName.length() > 0) {
			if(!mGesturesName.equals(mLoadedGestures.getName())) 
				mLoadedGestures.renameTo(new File(getFilesDir(), mGesturesName));
		}
	}
	
	private void checkForEmpty() {
		if (mAdapter.getCount() == 0) {
			mEmpty.setText(R.string.gestures_empty);
		}
	}
	
	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		
		if (mCurrentRenameGesture != null) {
			outState.putLong(GESTURES_INFO_ID, mCurrentRenameGesture.gesture.getID());
		}
	}
	
	@Override
	protected void onRestoreInstanceState(Bundle state) {
		super.onRestoreInstanceState(state);
		
		long id = state.getLong(GESTURES_INFO_ID, -1);
		if (id != -1) {
			final Set<String> entries = sStore.getGestureEntries();
			
out:        for (String name : entries) {
				for (Gesture gesture : sStore.getGestures(name)) {
					if (gesture.getID() == id) {
						mCurrentRenameGesture = new NamedGesture();
						mCurrentRenameGesture.name = name;
						mCurrentRenameGesture.gesture = gesture;
						break out;
					}
				}
			}
		}
	}
	
	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenu.ContextMenuInfo menuInfo) {
		
		super.onCreateContextMenu(menu, v, menuInfo);
		
		AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
		menu.setHeaderTitle(((TextView) info.targetView).getText());
		
		menu.add(0, MENU_ID_RENAME, 0, R.string.gestures_rename);
		menu.add(0, MENU_ID_REMOVE, 0, R.string.gestures_delete);
	}
	
	@Override
	public boolean onContextItemSelected(MenuItem item) {
		final AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)
		item.getMenuInfo();
		final NamedGesture gesture = (NamedGesture) menuInfo.targetView.getTag();
		
		switch (item.getItemId()) {
		case MENU_ID_RENAME:
			renameGesture(gesture);
			return true;
		case MENU_ID_REMOVE:
			deleteGesture(gesture);
			return true;
		}
		
		return super.onContextItemSelected(item);
	}
	
	private void renameGesture(NamedGesture gesture) {
		mCurrentRenameGesture = gesture;
		showDialog(DIALOG_RENAME_GESTURE);
	}
	
	@Override
	protected Dialog onCreateDialog(int id) {
		if (id == DIALOG_RENAME_GESTURE) {
			return createRenameDialog();
		}
		return super.onCreateDialog(id);
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);
		if (id == DIALOG_RENAME_GESTURE && mCurrentRenameGesture != null) {
			mInput.setText(mCurrentRenameGesture.name);
		}
	}

	private Dialog createRenameDialog() {
		final View layout = View.inflate(this, R.layout.dialog_rename, null);
		mInput = (EditText) layout.findViewById(R.id.name);
		((TextView) layout.findViewById(R.id.label)).setText(R.string.gestures_rename_label);

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setIcon(0);
		builder.setTitle(getString(R.string.gestures_rename_title));
		builder.setCancelable(true);
		builder.setOnCancelListener(new Dialog.OnCancelListener() {
			public void onCancel(DialogInterface dialog) {
				cleanupRenameDialog();
			}
		});
		builder.setNegativeButton(getString(R.string.cancel_action),
			new Dialog.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					cleanupRenameDialog();
				}
			}
		);
		builder.setPositiveButton(getString(R.string.rename_action),
			new Dialog.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					changeGestureName();
				}
			}
		);
		builder.setView(layout);
		return builder.create();
	}

	private void changeGestureName() {
		final String name = mInput.getText().toString();
		if (!TextUtils.isEmpty(name)) {
			final NamedGesture renameGesture = mCurrentRenameGesture;
			final GesturesAdapter adapter = mAdapter;
			final int count = adapter.getCount();

			// Simple linear search, there should not be enough items to warrant
			// a more sophisticated search
			for (int i = 0; i < count; i++) {
				final NamedGesture gesture = adapter.getItem(i);
				if (gesture.gesture.getID() == renameGesture.gesture.getID()) {
					sStore.removeGesture(gesture.name, gesture.gesture);
					gesture.name = mInput.getText().toString();
					sStore.addGesture(gesture.name, gesture.gesture);
					sStore.save();
					break;
				}
			}

			adapter.notifyDataSetChanged();
		}
		mCurrentRenameGesture = null;
	}

	private void cleanupRenameDialog() {
		if (mRenameDialog != null) {
			mRenameDialog.dismiss();
			mRenameDialog = null;
		}
		mCurrentRenameGesture = null;
	}

	private void deleteGesture(NamedGesture gesture) {
		sStore.removeGesture(gesture.name, gesture.gesture);
		sStore.save();

		final GesturesAdapter adapter = mAdapter;
		adapter.setNotifyOnChange(false);
		adapter.remove(gesture);
		adapter.sort(mSorter);
		checkForEmpty();
		adapter.notifyDataSetChanged();

		Toast.makeText(this, R.string.gestures_delete_success, Toast.LENGTH_SHORT).show();
	}

	private class GesturesLoadTask extends AsyncTask<Void, NamedGesture, Integer> {
		private final int THUMBNAIL_INSET = 12;
		private final int THUMBNAIL_SIZE = 96;

		
		private int mPathColor;

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			final Resources resources = getResources();
			mPathColor = resources.getColor(R.color.gesture_color);

			findViewById(R.id.addButton).setEnabled(false);
			findViewById(R.id.reloadButton).setEnabled(false);
			
			mAdapter.setNotifyOnChange(false);			
			mAdapter.clear();
		}

		@Override
		protected Integer doInBackground(Void... params) {
			if (isCancelled()) return STATUS_CANCELLED;
			if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
				return STATUS_NO_STORAGE;
			}

			final GestureLibrary store = sStore;

			if (store.load()) {
				for (String name : store.getGestureEntries()) {
					if (isCancelled()) break;

					for (Gesture gesture : store.getGestures(name)) {
						final Bitmap bitmap = gesture.toBitmap(THUMBNAIL_SIZE, THUMBNAIL_SIZE,
								THUMBNAIL_INSET, mPathColor);
						final NamedGesture namedGesture = new NamedGesture();
						namedGesture.gesture = gesture;
						namedGesture.name = name;

						mAdapter.addBitmap(namedGesture.gesture.getID(), bitmap);
						publishProgress(namedGesture);
					}
				}

				return STATUS_SUCCESS;
			}

			return STATUS_NOT_LOADED;
		}

		@Override
		protected void onProgressUpdate(NamedGesture... values) {
			super.onProgressUpdate(values);

			final GesturesAdapter adapter = mAdapter;
			adapter.setNotifyOnChange(false);

			for (NamedGesture gesture : values) {
				adapter.add(gesture);
			}

			adapter.sort(mSorter);
			adapter.notifyDataSetChanged();
		}

		@Override
		protected void onPostExecute(Integer result) {
			super.onPostExecute(result);

			if (result == STATUS_NO_STORAGE) {
				getListView().setVisibility(View.GONE);
				mEmpty.setVisibility(View.VISIBLE);
				mEmpty.setText(getString(R.string.gestures_error_loading,
						getIntent().getDataString()));
			} else {
				findViewById(R.id.addButton).setEnabled(true);
				findViewById(R.id.reloadButton).setEnabled(true);
				checkForEmpty();
			}
		}
	}

	static class NamedGesture {
		String name;
		Gesture gesture;
	}

	private class GesturesAdapter extends ArrayAdapter<NamedGesture> {
		private final LayoutInflater mInflater;
		private final Map<Long, Drawable> mThumbnails = Collections.synchronizedMap(
				new HashMap<Long, Drawable>());

		public GesturesAdapter(Context context) {
			super(context, 0);
			mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		}

		void addBitmap(Long id, Bitmap bitmap) {
			mThumbnails.put(id, new BitmapDrawable(bitmap));
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if (convertView == null) {
				convertView = mInflater.inflate(R.layout.gestures_item, parent, false);
			}

			final NamedGesture gesture = getItem(position);
			final TextView label = (TextView) convertView;

			label.setTag(gesture);
			label.setText(gesture.name);
			label.setCompoundDrawablesWithIntrinsicBounds(mThumbnails.get(gesture.gesture.getID()),
					null, null, null);

			return convertView;
		}
	}
}
